#pragma once
#include <iostream>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <cstdlib>
#include <arpa/inet.h>
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include "log.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"
#include "Daemon.hpp"
using namespace std;
const int defaultfd = -1;
const string defaultip = "0.0.0.0";
const uint16_t defaultport = 8080;
const int backlog = 5;
Log log;
enum
{
    SocketError = 2,
    BindError,
    ListenError,
    AcceptError
};

class TcpServer;
class ThreadData
{
public:
    ThreadData(const int sockfd, const uint16_t &port, const string &ip, TcpServer *t)
        : _sockfd(sockfd), _clientport(port), _clientIP(ip), _tsvr(t)
    {
    }

public:
    int _sockfd;
    string _clientIP;
    uint16_t _clientport;
    TcpServer *_tsvr;
};
class TcpServer
{
private:
    void service(int communication_sockfd, const string &clientip, const uint16_t &clientport)
    {
        // 测试代码：收到消息后服务器会有自动回复
        char buffer[1024];
        while (1)
        {
            ssize_t n = read(communication_sockfd, buffer, sizeof(buffer)); // 接收客户端发来的消息
            if (n > 0)
            {
                buffer[n] = '\0';
                cout << "client say:" << buffer << endl;

                // 将处理后的消息（加前缀）回发给客户端
                string reply = "TCP server reply:";
                reply += "I have got your message";
                write(communication_sockfd, reply.c_str(), reply.size());
            }
            else if (n == 0)
            {
                log(Info, "%s:%d quit, server close sockfd: %d", clientip.c_str(), clientport, communication_sockfd);
                break;
            }
            else
            {
                log(Warning, "read error, sockfd: %d", communication_sockfd);
            }
        }
        // close(communication_sockfd);
    }

    static void *handler(void *args)
    {
        // 无需在主线程中等待线程而是在子线程中分离线程的原因
        // 1. 多线程服务器中无需获取线程返回值，pthread_join的返回值获取功能无用，而pthread_detach可自动释放资源。
        // 2. pthread_join会阻塞主线程，影响新连接处理，降低并发效率，pthread_detach则无此问题。
        // 3. 用pthread_detach分离线程，能让线程结束后资源自动回收，避免类似僵尸进程的资源泄漏。
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData *>(args);
        td->_tsvr->service(td->_sockfd, td->_clientIP, td->_clientport);
        delete td;
        return nullptr;
    }

public:
    TcpServer(const uint16_t &port = defaultport, const string &ip = defaultip)
        : _listen_sockfd(defaultfd), _port(port), _ip(ip)
    {
        // init不放在构造中是为了构造中尽量不做有风险的事
    }
    void init()
    {
        _listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listen_sockfd < 0)
        {
            log(Fatal, "listen socket create error: %d, errstring: %s", errno, strerror(errno));
            exit(SocketError);
        }
        log(Info, "socket create successfully, listen sockfd: %d", _listen_sockfd);

        int opt = 1;
        setsockopt(_listen_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)); // 防止偶发性服务器无法立即重启

        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        inet_aton(_ip.c_str(), &(local.sin_addr));

        int ret = bind(_listen_sockfd, (struct sockaddr *)&local, sizeof(local));
        if (ret < 0)
        {
            log(Fatal, "listen socket bind error: %d, errstring: %s", errno, strerror(errno));
            exit(BindError);
        }

        log(Info, "bind socket successfully, sockfd: %d", _listen_sockfd);

        // 服务器一般比较“被动”，一致处于等待连接到来的状态
        if (listen(_listen_sockfd, backlog) < 0)
        {
            log(Fatal, "listen error: %d, errstring: %s", errno, strerror(errno));
            exit(ListenError);
        }
        log(Info, "listen successfully: %d", _listen_sockfd);
    }

    void start0() // 服务器启动
    {
        log(Info, "TCP server is running...");
        while (1)
        {
            // 1.获取监听
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int communication_sockfd = accept(_listen_sockfd, (struct sockaddr *)&client, &len); // 这里返回的sockfd是负责与每个客户具体通信的
            if (communication_sockfd < 0)
            {
                log(Warning, "accept error: %d, stringerror: %s", errno, strerror(errno));
                continue; // 不会因为一个客户端连接获取失败影响下一个客户端连接的获取
            }
            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));

            // 2.根据新连接进行通信
            log(Info, "get a new link... communication_sockfd: %d, client IP: %s, client port: %d", communication_sockfd, clientip, clientport);

            // version 1：单进程版本的服务器，同一时间只能让一个客户发消息
            service(communication_sockfd, clientip, clientport);
            close(communication_sockfd);
        }
    }

    void start1() // 服务器启动
    {
        log(Info, "TCP server is running...");
        while (1)
        {
            // 1.获取监听
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int communication_sockfd = accept(_listen_sockfd, (struct sockaddr *)&client, &len); // 这里返回的sockfd是负责与每个客户具体通信的
            if (communication_sockfd < 0)
            {
                log(Warning, "accept error: %d, stringerror: %s", errno, strerror(errno));
                continue; // 不会因为一个客户端连接获取失败影响下一个客户端连接的获取
            }
            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));

            // 2.根据新连接进行通信
            log(Info, "get a new link... communication_sockfd: %d, client IP: %s, client port: %d", communication_sockfd, clientip, clientport);

            // version 2：多进程版本的服务器，孙子进程负责通信
            pid_t id = fork();
            if (id == 0)
            {
                close(_listen_sockfd); // 子进程关监听套接字：它只负责通信，用不上监听功能，节约资源。
                if (fork() > 0)
                    exit(0);                                         // 避免子进程成为僵尸进程
                service(communication_sockfd, clientip, clientport); // 孙子进程负责通信，system领养
                close(communication_sockfd);
                exit(0);
            }
            close(communication_sockfd); // 父进程关通信套接字：通信交给子进程，父进程释放资源好继续接新连接。
            // father
            pid_t rid = waitpid(id, nullptr, 0);
        }
    }

    void start2() // 服务器启动
    {
        signal(SIGCHLD, SIG_IGN);
        log(Info, "TCP server is running...");
        while (1)
        {
            // 1.获取监听
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int communication_sockfd = accept(_listen_sockfd, (struct sockaddr *)&client, &len); // 这里返回的sockfd是负责与每个客户具体通信的
            if (communication_sockfd < 0)
            {
                log(Warning, "accept error: %d, stringerror: %s", errno, strerror(errno));
                continue; // 不会因为一个客户端连接获取失败影响下一个客户端连接的获取
            }
            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));

            // 2.根据新连接进行通信
            log(Info, "get a new link... communication_sockfd: %d, client IP: %s, client port: %d", communication_sockfd, clientip, clientport);

            // version 2：多进程版本的服务器，系统自动回收子进程
            pid_t id = fork();
            if (id == 0)
            {
                close(_listen_sockfd);                               // 子进程关监听套接字：它只负责通信，用不上监听功能，节约资源。
                service(communication_sockfd, clientip, clientport); // 子进程直接处理通信
                close(communication_sockfd);
                exit(0);
            }
            close(communication_sockfd); // 父进程关通信套接字：通信交给子进程，父进程释放资源好继续接新连接。
            // 去掉waitpid，依赖SIGCHLD忽略实现自动回收
        }
    }

    void start3() // 服务器启动
    {
        log(Info, "TCP server is running...");
        while (1)
        {
            // 1.获取监听
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int communication_sockfd = accept(_listen_sockfd, (struct sockaddr *)&client, &len); // 这里返回的sockfd是负责与每个客户具体通信的
            if (communication_sockfd < 0)
            {
                log(Warning, "accept error: %d, stringerror: %s", errno, strerror(errno));
                continue; // 不会因为一个客户端连接获取失败影响下一个客户端连接的获取
            }
            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));

            // 2.根据新连接进行通信
            log(Info, "get a new link... communication_sockfd: %d, client IP: %s, client port: %d", communication_sockfd, clientip, clientport);

            // version 3：多线程版本的服务器，即时创建线程
            ThreadData *td = new ThreadData(communication_sockfd, clientport, clientip, this);
            pthread_t tid;
            pthread_create(&tid, nullptr, handler, td);
        }
    }

    void start4() // 服务器启动
    {
        daemon();
        signal(SIGPIPE, SIG_IGN);
        ThreadPool<Task>::getInstance()->start();
        log(Info, "TCP server is running...");
        while (1)
        {
            // 1.获取监听
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int communication_sockfd = accept(_listen_sockfd, (struct sockaddr *)&client, &len); // 这里返回的sockfd是负责与每个客户具体通信的
            if (communication_sockfd < 0)
            {
                log(Warning, "accept error: %d, stringerror: %s", errno, strerror(errno));
                continue; // 不会因为一个客户端连接获取失败影响下一个客户端连接的获取
            }
            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));

            // 2.根据新连接进行通信
            log(Info, "get a new link... communication_sockfd: %d, client IP: %s, client port: %d", communication_sockfd, clientip, clientport);
            //cout << "hello world" << endl;
            // version 4：线程池版本
            Task t(communication_sockfd, clientip, clientport);
            ThreadPool<Task>::getInstance()->push(t);
        }
    }

    ~TcpServer()
    {
    }

private:
    int _listen_sockfd;
    uint16_t _port;
    string _ip;
};